﻿using System;
using System.Runtime.InteropServices;
using Pfz.DataTypes;

namespace Pfz.Caching
{
	/// <summary>
	/// This is the struct that implements the code for ThreadUnsafeReference and
	/// Reference.
	/// </summary>
	public struct ThreadUnsafeReferenceSlim<T>:
		IReference<T>,
		IEquatable<IReference<T>>,
		#if !SILVERLIGHT
			ICloneable<ThreadUnsafeReferenceSlim<T>>,
		#endif
		IDisposable
	where
		T: class
	{
		private object _obj;
		private GCHandleType _handleType;

		/// <summary>
		/// Creates a new reference using the given value and handle type.
		/// </summary>
		public ThreadUnsafeReferenceSlim(T value, GCHandleType handleType)
		{
			_handleType = handleType;

			switch(handleType)
			{
				case GCHandleType.Normal:
					_obj = value;
					break;

				case GCHandleType.Weak:
					_obj = new WeakReference(value);
					break;

				default:
					throw new ArgumentException();
			}
		}

		/// <summary>
		/// Frees the handle immediatelly.
		/// </summary>
		public void Dispose()
		{
			_obj = null;
		}
		
		/// <summary>
		/// Gets or sets the value pointed by the handle.
		/// </summary>
		public T Value
		{
			get
			{
				var obj = _obj;
				if (obj == null)
					return null;

				WeakReference wr = obj as WeakReference;
				if (wr != null)
					return (T)wr.Target;

				return (T)obj;
			}
			set
			{
				if (_handleType == GCHandleType.Weak)
					_obj = new WeakReference(value);
				else
					_obj = value;
			}
		}
		
		/// <summary>
		/// Gets or sets the handle type.
		/// </summary>
		public GCHandleType HandleType
		{
			get
			{
				return _handleType;
			}
			set
			{
				if (value == _handleType)
					return;
				
				switch(value)
				{
					case GCHandleType.Normal:
						_obj = Value;
						break;

					case GCHandleType.Weak:
						_obj = new WeakReference(Value);
						break;

					default:
						throw new ArgumentException();
				}

				_handleType = value;
			}
		}

		/// <summary>
		/// Gets the data of this Reference.
		/// </summary>
		public ReferenceData<T> GetData()
		{
			return new ReferenceData<T>(Value, _handleType);
		}

		/// <summary>
		/// Sets the data of this reference.
		/// </summary>
		public void Set(GCHandleType handleType, T value)
		{
			switch(handleType)
			{
				case GCHandleType.Normal:
					_obj = value;
					break;

				case GCHandleType.Weak:
					_obj = new WeakReference(value);
					break;

				default:
					throw new ArgumentException();
			}

			_handleType = handleType;
		}

		/// <summary>
		/// Gets the HashCode of the Value.
		/// </summary>
		public override int GetHashCode()
		{
			var value = Value;
			if (value == null)
				return 0;

			return value.GetHashCode();
		}

		/// <summary>
		/// Compares the Value of this reference with the value of another reference.
		/// </summary>
		public override bool Equals(object obj)
		{
			var other = obj as IReference<T>;
			if (other != null)
				return Equals(other);

			return false;
		}

		/// <summary>
		/// Compares the Value of this reference with the value of another reference.
		/// </summary>
		public bool Equals(IReference<T> other)
		{
			if (other == null)
				return false;

			return Value == other.Value;
		}
		
		#region IValueContainer Members
			object IValueContainer.Value
			{
				get
				{
					return Value;
				}
				set
				{
					Value = (T)value;
				}
			}
		#endregion
		#region ICloneable<ThreadUnsafeReference<T>> Members
			/// <summary>
			/// Creates a copy of this reference.
			/// </summary>
			public ThreadUnsafeReferenceSlim<T> Clone()
			{
				return new ThreadUnsafeReferenceSlim<T>(Value, _handleType);
			}
		#endregion
		#region ICloneable Members
			#if !SILVERLIGHT
			object ICloneable.Clone()
			{
				return Clone();
			}
			#endif
		#endregion
		#region IReference Members
			void IReference.Set(GCHandleType handleType, object value)
			{
				Set(handleType, (T)value);
			}
			IReferenceData IReference.GetData()
			{
				return GetData();
			}
		#endregion
	}
}
